/***********************************************************************************************************************
*  OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
*  See also https://openstudio.net/license
***********************************************************************************************************************/

#include "CoilPerformanceDXCooling.hpp"
#include "CoilPerformanceDXCooling_Impl.hpp"
#include "Model.hpp"
#include "Model_Impl.hpp"
#include "Curve.hpp"
#include "Curve_Impl.hpp"
#include "CurveBiquadratic.hpp"
#include "CurveBiquadratic_Impl.hpp"
#include "CurveQuadratic.hpp"
#include "CurveQuadratic_Impl.hpp"
#include "Node.hpp"
#include "Node_Impl.hpp"
#include "CoilCoolingDXTwoStageWithHumidityControlMode.hpp"
#include "CoilCoolingDXTwoStageWithHumidityControlMode_Impl.hpp"

#include "../utilities/units/Unit.hpp"
#include "../utilities/core/Assert.hpp"
#include "../utilities/sql/SqlFile.hpp"

#include <utilities/idd/IddFactory.hxx>
#include <utilities/idd/OS_CoilPerformance_DX_Cooling_FieldEnums.hxx>

#include <fmt/core.h>

namespace openstudio {
namespace model {

  namespace detail {

    CoilPerformanceDXCooling_Impl::CoilPerformanceDXCooling_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle)
      : ParentObject_Impl(idfObject, model, keepHandle) {
      OS_ASSERT(idfObject.iddObject().type() == CoilPerformanceDXCooling::iddObjectType());
    }

    CoilPerformanceDXCooling_Impl::CoilPerformanceDXCooling_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model,
                                                                 bool keepHandle)
      : ParentObject_Impl(other, model, keepHandle) {
      OS_ASSERT(other.iddObject().type() == CoilPerformanceDXCooling::iddObjectType());
    }

    CoilPerformanceDXCooling_Impl::CoilPerformanceDXCooling_Impl(const CoilPerformanceDXCooling_Impl& other, Model_Impl* model, bool keepHandle)
      : ParentObject_Impl(other, model, keepHandle) {}

    const std::vector<std::string>& CoilPerformanceDXCooling_Impl::outputVariableNames() const {
      static const std::vector<std::string> result;
      return result;
    }

    IddObjectType CoilPerformanceDXCooling_Impl::iddObjectType() const {
      return CoilPerformanceDXCooling::iddObjectType();
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::grossRatedTotalCoolingCapacity() const {
      return getDouble(OS_CoilPerformance_DX_CoolingFields::GrossRatedTotalCoolingCapacity, true);
    }

    bool CoilPerformanceDXCooling_Impl::isGrossRatedTotalCoolingCapacityAutosized() const {
      bool result = false;
      boost::optional<std::string> value = getString(OS_CoilPerformance_DX_CoolingFields::GrossRatedTotalCoolingCapacity, true);
      if (value) {
        result = openstudio::istringEqual(value.get(), "autosize");
      }
      return result;
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::grossRatedSensibleHeatRatio() const {
      return getDouble(OS_CoilPerformance_DX_CoolingFields::GrossRatedSensibleHeatRatio, true);
    }

    bool CoilPerformanceDXCooling_Impl::isGrossRatedSensibleHeatRatioAutosized() const {
      bool result = false;
      boost::optional<std::string> value = getString(OS_CoilPerformance_DX_CoolingFields::GrossRatedSensibleHeatRatio, true);
      if (value) {
        result = openstudio::istringEqual(value.get(), "autosize");
      }
      return result;
    }

    double CoilPerformanceDXCooling_Impl::grossRatedCoolingCOP() const {
      boost::optional<double> value = getDouble(OS_CoilPerformance_DX_CoolingFields::GrossRatedCoolingCOP, true);
      OS_ASSERT(value);
      return value.get();
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::ratedAirFlowRate() const {
      return getDouble(OS_CoilPerformance_DX_CoolingFields::RatedAirFlowRate, true);
    }

    bool CoilPerformanceDXCooling_Impl::isRatedAirFlowRateAutosized() const {
      bool result = false;
      boost::optional<std::string> value = getString(OS_CoilPerformance_DX_CoolingFields::RatedAirFlowRate, true);
      if (value) {
        result = openstudio::istringEqual(value.get(), "autosize");
      }
      return result;
    }

    double CoilPerformanceDXCooling_Impl::fractionofAirFlowBypassedAroundCoil() const {
      boost::optional<double> value = getDouble(OS_CoilPerformance_DX_CoolingFields::FractionofAirFlowBypassedAroundCoil, true);
      OS_ASSERT(value);
      return value.get();
    }

    Curve CoilPerformanceDXCooling_Impl::totalCoolingCapacityFunctionofTemperatureCurve() const {
      boost::optional<Curve> value = optionalTotalCoolingCapacityFunctionofTemperatureCurve();
      if (!value) {
        LOG_AND_THROW(briefDescription() << " does not have an Total Cooling Capacity Functionof Temperature Curve attached.");
      }
      return value.get();
    }

    Curve CoilPerformanceDXCooling_Impl::totalCoolingCapacityFunctionofFlowFractionCurve() const {
      boost::optional<Curve> value = optionalTotalCoolingCapacityFunctionofFlowFractionCurve();
      if (!value) {
        LOG_AND_THROW(briefDescription() << " does not have an Total Cooling Capacity Functionof Flow Fraction Curve attached.");
      }
      return value.get();
    }

    Curve CoilPerformanceDXCooling_Impl::energyInputRatioFunctionofTemperatureCurve() const {
      boost::optional<Curve> value = optionalEnergyInputRatioFunctionofTemperatureCurve();
      if (!value) {
        LOG_AND_THROW(briefDescription() << " does not have an Energy Input Ratio Functionof Temperature Curve attached.");
      }
      return value.get();
    }

    Curve CoilPerformanceDXCooling_Impl::energyInputRatioFunctionofFlowFractionCurve() const {
      boost::optional<Curve> value = optionalEnergyInputRatioFunctionofFlowFractionCurve();
      if (!value) {
        LOG_AND_THROW(briefDescription() << " does not have an Energy Input Ratio Functionof Flow Fraction Curve attached.");
      }
      return value.get();
    }

    Curve CoilPerformanceDXCooling_Impl::partLoadFractionCorrelationCurve() const {
      boost::optional<Curve> value = optionalPartLoadFractionCorrelationCurve();
      if (!value) {
        LOG_AND_THROW(briefDescription() << " does not have an Part Load Fraction Correlation Curve attached.");
      }
      return value.get();
    }

    double CoilPerformanceDXCooling_Impl::nominalTimeforCondensateRemovaltoBegin() const {
      boost::optional<double> value = getDouble(OS_CoilPerformance_DX_CoolingFields::NominalTimeforCondensateRemovaltoBegin, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CoilPerformanceDXCooling_Impl::ratioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity() const {
      boost::optional<double> value =
        getDouble(OS_CoilPerformance_DX_CoolingFields::RatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CoilPerformanceDXCooling_Impl::maximumCyclingRate() const {
      boost::optional<double> value = getDouble(OS_CoilPerformance_DX_CoolingFields::MaximumCyclingRate, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CoilPerformanceDXCooling_Impl::latentCapacityTimeConstant() const {
      boost::optional<double> value = getDouble(OS_CoilPerformance_DX_CoolingFields::LatentCapacityTimeConstant, true);
      OS_ASSERT(value);
      return value.get();
    }

    boost::optional<Node> CoilPerformanceDXCooling_Impl::condenserAirInletNode() const {
      return getObject<ModelObject>().getModelObjectTarget<Node>(OS_CoilPerformance_DX_CoolingFields::CondenserAirInletNode);
    }

    std::string CoilPerformanceDXCooling_Impl::condenserType() const {
      boost::optional<std::string> value = getString(OS_CoilPerformance_DX_CoolingFields::CondenserType, true);
      OS_ASSERT(value);
      return value.get();
    }

    double CoilPerformanceDXCooling_Impl::evaporativeCondenserEffectiveness() const {
      boost::optional<double> value = getDouble(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserEffectiveness, true);
      OS_ASSERT(value);
      return value.get();
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::evaporativeCondenserAirFlowRate() const {
      return getDouble(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserAirFlowRate, true);
    }

    bool CoilPerformanceDXCooling_Impl::isEvaporativeCondenserAirFlowRateAutosized() const {
      bool result = false;
      boost::optional<std::string> value = getString(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserAirFlowRate, true);
      if (value) {
        result = openstudio::istringEqual(value.get(), "autosize");
      }
      return result;
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::evaporativeCondenserPumpRatedPowerConsumption() const {
      return getDouble(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserPumpRatedPowerConsumption, true);
    }

    bool CoilPerformanceDXCooling_Impl::isEvaporativeCondenserPumpRatedPowerConsumptionAutosized() const {
      bool result = false;
      boost::optional<std::string> value = getString(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserPumpRatedPowerConsumption, true);
      if (value) {
        result = openstudio::istringEqual(value.get(), "autosize");
      }
      return result;
    }

    boost::optional<Curve> CoilPerformanceDXCooling_Impl::sensibleHeatRatioFunctionofTemperatureCurve() const {
      return getObject<ModelObject>().getModelObjectTarget<Curve>(OS_CoilPerformance_DX_CoolingFields::SensibleHeatRatioFunctionofTemperatureCurve);
    }

    boost::optional<Curve> CoilPerformanceDXCooling_Impl::sensibleHeatRatioFunctionofFlowFractionCurve() const {
      return getObject<ModelObject>().getModelObjectTarget<Curve>(OS_CoilPerformance_DX_CoolingFields::SensibleHeatRatioFunctionofFlowFractionCurve);
    }

    bool CoilPerformanceDXCooling_Impl::setGrossRatedTotalCoolingCapacity(boost::optional<double> grossRatedTotalCoolingCapacity) {
      bool result(false);
      if (grossRatedTotalCoolingCapacity) {
        result = setDouble(OS_CoilPerformance_DX_CoolingFields::GrossRatedTotalCoolingCapacity, grossRatedTotalCoolingCapacity.get());
      }
      return result;
    }

    void CoilPerformanceDXCooling_Impl::autosizeGrossRatedTotalCoolingCapacity() {
      bool result = setString(OS_CoilPerformance_DX_CoolingFields::GrossRatedTotalCoolingCapacity, "autosize");
      OS_ASSERT(result);
    }

    bool CoilPerformanceDXCooling_Impl::setGrossRatedSensibleHeatRatio(boost::optional<double> grossRatedSensibleHeatRatio) {
      bool result(false);
      if (grossRatedSensibleHeatRatio) {
        result = setDouble(OS_CoilPerformance_DX_CoolingFields::GrossRatedSensibleHeatRatio, grossRatedSensibleHeatRatio.get());
      }
      return result;
    }

    void CoilPerformanceDXCooling_Impl::autosizeGrossRatedSensibleHeatRatio() {
      bool result = setString(OS_CoilPerformance_DX_CoolingFields::GrossRatedSensibleHeatRatio, "autosize");
      OS_ASSERT(result);
    }

    bool CoilPerformanceDXCooling_Impl::setGrossRatedCoolingCOP(double grossRatedCoolingCOP) {
      bool result = setDouble(OS_CoilPerformance_DX_CoolingFields::GrossRatedCoolingCOP, grossRatedCoolingCOP);
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setRatedAirFlowRate(boost::optional<double> ratedAirFlowRate) {
      bool result(false);
      if (ratedAirFlowRate) {
        result = setDouble(OS_CoilPerformance_DX_CoolingFields::RatedAirFlowRate, ratedAirFlowRate.get());
      }
      return result;
    }

    void CoilPerformanceDXCooling_Impl::autosizeRatedAirFlowRate() {
      bool result = setString(OS_CoilPerformance_DX_CoolingFields::RatedAirFlowRate, "autosize");
      OS_ASSERT(result);
    }

    bool CoilPerformanceDXCooling_Impl::setFractionofAirFlowBypassedAroundCoil(double fractionofAirFlowBypassedAroundCoil) {
      bool result = setDouble(OS_CoilPerformance_DX_CoolingFields::FractionofAirFlowBypassedAroundCoil, fractionofAirFlowBypassedAroundCoil);
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setTotalCoolingCapacityFunctionofTemperatureCurve(const Curve& curve) {
      bool result = setPointer(OS_CoilPerformance_DX_CoolingFields::TotalCoolingCapacityFunctionofTemperatureCurve, curve.handle());
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setTotalCoolingCapacityFunctionofFlowFractionCurve(const Curve& curve) {
      bool result = setPointer(OS_CoilPerformance_DX_CoolingFields::TotalCoolingCapacityFunctionofFlowFractionCurve, curve.handle());
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setEnergyInputRatioFunctionofTemperatureCurve(const Curve& curve) {
      bool result = setPointer(OS_CoilPerformance_DX_CoolingFields::EnergyInputRatioFunctionofTemperatureCurve, curve.handle());
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setEnergyInputRatioFunctionofFlowFractionCurve(const Curve& curve) {
      bool result = setPointer(OS_CoilPerformance_DX_CoolingFields::EnergyInputRatioFunctionofFlowFractionCurve, curve.handle());
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setPartLoadFractionCorrelationCurve(const Curve& curve) {
      bool result = setPointer(OS_CoilPerformance_DX_CoolingFields::PartLoadFractionCorrelationCurve, curve.handle());
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setNominalTimeforCondensateRemovaltoBegin(double nominalTimeforCondensateRemovaltoBegin) {
      bool result = setDouble(OS_CoilPerformance_DX_CoolingFields::NominalTimeforCondensateRemovaltoBegin, nominalTimeforCondensateRemovaltoBegin);
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(
      double ratioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity) {
      bool result = setDouble(OS_CoilPerformance_DX_CoolingFields::RatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity,
                              ratioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity);
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setMaximumCyclingRate(double maximumCyclingRate) {
      bool result = setDouble(OS_CoilPerformance_DX_CoolingFields::MaximumCyclingRate, maximumCyclingRate);
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setLatentCapacityTimeConstant(double latentCapacityTimeConstant) {
      bool result = setDouble(OS_CoilPerformance_DX_CoolingFields::LatentCapacityTimeConstant, latentCapacityTimeConstant);
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setCondenserAirInletNode(const boost::optional<Node>& node) {
      bool result(false);
      if (node) {
        result = setPointer(OS_CoilPerformance_DX_CoolingFields::CondenserAirInletNode, node.get().handle());
      } else {
        resetCondenserAirInletNode();
        result = true;
      }
      return result;
    }

    void CoilPerformanceDXCooling_Impl::resetCondenserAirInletNode() {
      bool result = setString(OS_CoilPerformance_DX_CoolingFields::CondenserAirInletNode, "");
      OS_ASSERT(result);
    }

    bool CoilPerformanceDXCooling_Impl::setCondenserType(const std::string& condenserType) {
      bool result = setString(OS_CoilPerformance_DX_CoolingFields::CondenserType, condenserType);
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setEvaporativeCondenserEffectiveness(double evaporativeCondenserEffectiveness) {
      bool result = setDouble(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserEffectiveness, evaporativeCondenserEffectiveness);
      return result;
    }

    bool CoilPerformanceDXCooling_Impl::setEvaporativeCondenserAirFlowRate(boost::optional<double> evaporativeCondenserAirFlowRate) {
      bool result(false);
      if (evaporativeCondenserAirFlowRate) {
        result = setDouble(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserAirFlowRate, evaporativeCondenserAirFlowRate.get());
      }
      return result;
    }

    void CoilPerformanceDXCooling_Impl::autosizeEvaporativeCondenserAirFlowRate() {
      bool result = setString(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserAirFlowRate, "autosize");
      OS_ASSERT(result);
    }

    bool CoilPerformanceDXCooling_Impl::setEvaporativeCondenserPumpRatedPowerConsumption(
      boost::optional<double> evaporativeCondenserPumpRatedPowerConsumption) {
      bool result(false);
      if (evaporativeCondenserPumpRatedPowerConsumption) {
        result = setDouble(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserPumpRatedPowerConsumption,
                           evaporativeCondenserPumpRatedPowerConsumption.get());
      }
      return result;
    }

    void CoilPerformanceDXCooling_Impl::autosizeEvaporativeCondenserPumpRatedPowerConsumption() {
      bool result = setString(OS_CoilPerformance_DX_CoolingFields::EvaporativeCondenserPumpRatedPowerConsumption, "autosize");
      OS_ASSERT(result);
    }

    bool CoilPerformanceDXCooling_Impl::setSensibleHeatRatioFunctionofTemperatureCurve(const boost::optional<Curve>& curve) {
      bool result(false);
      if (curve) {
        result = setPointer(OS_CoilPerformance_DX_CoolingFields::SensibleHeatRatioFunctionofTemperatureCurve, curve.get().handle());
      } else {
        resetSensibleHeatRatioFunctionofTemperatureCurve();
        result = true;
      }
      return result;
    }

    void CoilPerformanceDXCooling_Impl::resetSensibleHeatRatioFunctionofTemperatureCurve() {
      bool result = setString(OS_CoilPerformance_DX_CoolingFields::SensibleHeatRatioFunctionofTemperatureCurve, "");
      OS_ASSERT(result);
    }

    bool CoilPerformanceDXCooling_Impl::setSensibleHeatRatioFunctionofFlowFractionCurve(const boost::optional<Curve>& curve) {
      bool result(false);
      if (curve) {
        result = setPointer(OS_CoilPerformance_DX_CoolingFields::SensibleHeatRatioFunctionofFlowFractionCurve, curve.get().handle());
      } else {
        resetSensibleHeatRatioFunctionofFlowFractionCurve();
        result = true;
      }
      return result;
    }

    void CoilPerformanceDXCooling_Impl::resetSensibleHeatRatioFunctionofFlowFractionCurve() {
      bool result = setString(OS_CoilPerformance_DX_CoolingFields::SensibleHeatRatioFunctionofFlowFractionCurve, "");
      OS_ASSERT(result);
    }

    boost::optional<Curve> CoilPerformanceDXCooling_Impl::optionalTotalCoolingCapacityFunctionofTemperatureCurve() const {
      return getObject<ModelObject>().getModelObjectTarget<Curve>(
        OS_CoilPerformance_DX_CoolingFields::TotalCoolingCapacityFunctionofTemperatureCurve);
    }

    boost::optional<Curve> CoilPerformanceDXCooling_Impl::optionalTotalCoolingCapacityFunctionofFlowFractionCurve() const {
      return getObject<ModelObject>().getModelObjectTarget<Curve>(
        OS_CoilPerformance_DX_CoolingFields::TotalCoolingCapacityFunctionofFlowFractionCurve);
    }

    boost::optional<Curve> CoilPerformanceDXCooling_Impl::optionalEnergyInputRatioFunctionofTemperatureCurve() const {
      return getObject<ModelObject>().getModelObjectTarget<Curve>(OS_CoilPerformance_DX_CoolingFields::EnergyInputRatioFunctionofTemperatureCurve);
    }

    boost::optional<Curve> CoilPerformanceDXCooling_Impl::optionalEnergyInputRatioFunctionofFlowFractionCurve() const {
      return getObject<ModelObject>().getModelObjectTarget<Curve>(OS_CoilPerformance_DX_CoolingFields::EnergyInputRatioFunctionofFlowFractionCurve);
    }

    boost::optional<Curve> CoilPerformanceDXCooling_Impl::optionalPartLoadFractionCorrelationCurve() const {
      return getObject<ModelObject>().getModelObjectTarget<Curve>(OS_CoilPerformance_DX_CoolingFields::PartLoadFractionCorrelationCurve);
    }

    std::vector<ModelObject> CoilPerformanceDXCooling_Impl::children() const {
      std::vector<ModelObject> result;
      {
        auto mo = totalCoolingCapacityFunctionofTemperatureCurve();
        result.push_back(mo);
      }
      {
        auto mo = totalCoolingCapacityFunctionofFlowFractionCurve();
        result.push_back(mo);
      }
      {
        auto mo = energyInputRatioFunctionofTemperatureCurve();
        result.push_back(mo);
      }
      {
        auto mo = energyInputRatioFunctionofFlowFractionCurve();
        result.push_back(mo);
      }
      {
        auto mo = partLoadFractionCorrelationCurve();
        result.push_back(mo);
      }

      if (auto mo = sensibleHeatRatioFunctionofTemperatureCurve()) {
        result.push_back(mo.get());
      }
      if (auto mo = sensibleHeatRatioFunctionofFlowFractionCurve()) {
        result.push_back(mo.get());
      }
      return result;
    }

    ModelObject CoilPerformanceDXCooling_Impl::clone(Model model) const {
      auto newObject = ModelObject_Impl::clone(model).cast<CoilPerformanceDXCooling>();

      return std::move(newObject);
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::getAutosizedValueCustom(std::string valueName, std::string units) const {
      // Check that the model has a sql file
      if (!model().sqlFile()) {
        LOG(Warn, "This model has no sql file, cannot retrieve the autosized value '" << valueName << "'.");
        return boost::none;
      }

      auto parentCoils = getObject<CoilPerformanceDXCooling>().getModelObjectSources<CoilCoolingDXTwoStageWithHumidityControlMode>();
      if (parentCoils.empty()) {
        LOG(Warn, briefDescription() << " does not have a parent CoilCoolingDXTwoStageWithHumidityControlMode, cannot retrieve the autosized value.");
        return boost::none;
      }
      if (parentCoils.size() > 1) {
        LOG(Warn, "For " << briefDescription() << ", more than one CoilCoolingDXTwoStageWithHumidityControlMode are using it, returning the first");
      }

      // Get the object name and transform to the way it is recorded
      // in the sql file
      std::string sqlName = name().get();
      boost::to_upper(sqlName);

      std::string parSqlName = parentCoils.front().nameString();
      boost::to_upper(parSqlName);
      // Join the parent and child object names, like:
      // COIL COOLING DX TWO STAGE WITH HUMIDITY CONTROL MODE 1:COIL PERFORMANCE DX COOLING 1
      sqlName = parSqlName + std::string(":") + sqlName;

      const std::string sqlObjectType = "Coil:Cooling:DX:TwoStageWithHumidityControlMode";

      const std::string directQuery = R"sql(
      SELECT Value FROM ComponentSizes
        WHERE CompType = ?
          AND CompName = ?
          AND Description = ?
          AND Units = ?;
    )sql";
      boost::optional<double> val = model().sqlFile().get().execAndReturnFirstDouble(directQuery,
                                                                                     // bindArgs
                                                                                     sqlObjectType, sqlName, valueName, units);
      if (!val) {
        LOG(Debug, fmt::format(R"sql(The direct query failed:
SELECT Value FROM ComponentSizes
  WHERE CompType = '{}'
    AND CompName = '{}'
    AND Description = '{}'
    AND Units = '{}';)sql",
                               sqlObjectType, sqlName, valueName, units));
      }
      return val;
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::autosizedGrossRatedTotalCoolingCapacity() const {
      return getAutosizedValueCustom("Design Size Gross Rated Total Cooling Capacity", "W");
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::autosizedGrossRatedSensibleHeatRatio() const {
      return getAutosizedValueCustom("Design Size Gross Rated Sensible Heat Ratio", "");
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::autosizedRatedAirFlowRate() const {
      return getAutosizedValueCustom("Design Size Rated Air Flow Rate", "m3/s");
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::autosizedEvaporativeCondenserAirFlowRate() const {
      return getAutosizedValueCustom("Design Size Evaporative Condenser Air Flow Rate", "m3/s");
    }

    boost::optional<double> CoilPerformanceDXCooling_Impl::autosizedEvaporativeCondenserPumpRatedPowerConsumption() const {
      return getAutosizedValueCustom("Design Size Evaporative Condenser Pump Rated Power Consumption", "W");
    }

    void CoilPerformanceDXCooling_Impl::autosize() {
      autosizeGrossRatedTotalCoolingCapacity();
      autosizeGrossRatedSensibleHeatRatio();
      autosizeRatedAirFlowRate();
      autosizeEvaporativeCondenserAirFlowRate();
      autosizeEvaporativeCondenserPumpRatedPowerConsumption();
    }

    void CoilPerformanceDXCooling_Impl::applySizingValues() {
      boost::optional<double> val;
      val = autosizedGrossRatedTotalCoolingCapacity();
      if (val) {
        setGrossRatedTotalCoolingCapacity(val.get());
      }

      val = autosizedGrossRatedSensibleHeatRatio();
      if (val) {
        setGrossRatedSensibleHeatRatio(val.get());
      }

      val = autosizedRatedAirFlowRate();
      if (val) {
        setRatedAirFlowRate(val.get());
      }

      val = autosizedEvaporativeCondenserAirFlowRate();
      if (val) {
        setEvaporativeCondenserAirFlowRate(val.get());
      }

      val = autosizedEvaporativeCondenserPumpRatedPowerConsumption();
      if (val) {
        setEvaporativeCondenserPumpRatedPowerConsumption(val.get());
      }
    }

  }  // namespace detail

  CoilPerformanceDXCooling::CoilPerformanceDXCooling(const Model& model, const Curve& coolingCapacityFunctionofTemperature,
                                                     const Curve& coolingCapacityFunctionofFlowFraction,
                                                     const Curve& energyInputRatioFunctionofTemperature,
                                                     const Curve& energyInputRatioFunctionofFlowFraction, const Curve& partLoadFractionCorrelation)
    : ParentObject(CoilPerformanceDXCooling::iddObjectType(), model) {
    OS_ASSERT(getImpl<detail::CoilPerformanceDXCooling_Impl>());

    autosizeGrossRatedTotalCoolingCapacity();
    autosizeGrossRatedSensibleHeatRatio();
    setGrossRatedCoolingCOP(3.0);
    autosizeRatedAirFlowRate();
    setFractionofAirFlowBypassedAroundCoil(0.0);
    setNominalTimeforCondensateRemovaltoBegin(1000.0);
    setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(1.5);
    setMaximumCyclingRate(3.0);
    setLatentCapacityTimeConstant(45);
    setCondenserType("AirCooled");
    setEvaporativeCondenserEffectiveness(0.9);
    autosizeEvaporativeCondenserAirFlowRate();
    autosizeEvaporativeCondenserPumpRatedPowerConsumption();

    setTotalCoolingCapacityFunctionofTemperatureCurve(coolingCapacityFunctionofTemperature);
    setTotalCoolingCapacityFunctionofFlowFractionCurve(coolingCapacityFunctionofFlowFraction);
    setEnergyInputRatioFunctionofTemperatureCurve(energyInputRatioFunctionofTemperature);
    setEnergyInputRatioFunctionofFlowFractionCurve(energyInputRatioFunctionofFlowFraction);
    setPartLoadFractionCorrelationCurve(partLoadFractionCorrelation);
  }

  CoilPerformanceDXCooling::CoilPerformanceDXCooling(const Model& model) : ParentObject(CoilPerformanceDXCooling::iddObjectType(), model) {
    OS_ASSERT(getImpl<detail::CoilPerformanceDXCooling_Impl>());

    autosizeGrossRatedTotalCoolingCapacity();
    autosizeGrossRatedSensibleHeatRatio();
    setGrossRatedCoolingCOP(3.0);
    autosizeRatedAirFlowRate();
    setFractionofAirFlowBypassedAroundCoil(0.0);
    setNominalTimeforCondensateRemovaltoBegin(1000.0);
    setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(1.5);
    setMaximumCyclingRate(3.0);
    setLatentCapacityTimeConstant(45);
    setCondenserType("AirCooled");
    setEvaporativeCondenserEffectiveness(0.9);
    autosizeEvaporativeCondenserAirFlowRate();
    autosizeEvaporativeCondenserPumpRatedPowerConsumption();

    {
      CurveBiquadratic curve(model);
      curve.setCoefficient1Constant(1.067939449);
      curve.setCoefficient2x(-0.031261829);
      curve.setCoefficient3xPOW2(0.001974308);
      curve.setCoefficient4y(-0.002726426);
      curve.setCoefficient5yPOW2(-5.52654E-05);
      curve.setCoefficient6xTIMESY(-6.31169E-05);
      curve.setMinimumValueofx(-100);
      curve.setMaximumValueofx(100);
      curve.setMinimumValueofy(-100);
      curve.setMaximumValueofy(100);
      setTotalCoolingCapacityFunctionofTemperatureCurve(curve);
    }

    {
      CurveQuadratic curve(model);
      curve.setCoefficient1Constant(0.8);
      curve.setCoefficient2x(0.2);
      curve.setCoefficient3xPOW2(0.0);
      curve.setMinimumValueofx(0.5);
      curve.setMaximumValueofx(1.5);
      setTotalCoolingCapacityFunctionofFlowFractionCurve(curve);
    }

    {
      CurveBiquadratic curve(model);
      curve.setCoefficient1Constant(0.174059889);
      curve.setCoefficient2x(0.022281508);
      curve.setCoefficient3xPOW2(-0.000134077);
      curve.setCoefficient4y(0.028298025);
      curve.setCoefficient5yPOW2(0.000485106);
      curve.setCoefficient6xTIMESY(-0.001677095);
      curve.setMinimumValueofx(-100);
      curve.setMaximumValueofx(100);
      curve.setMinimumValueofy(-100);
      curve.setMaximumValueofy(100);
      setEnergyInputRatioFunctionofTemperatureCurve(curve);
    }

    {
      CurveQuadratic curve(model);
      curve.setCoefficient1Constant(1552);
      curve.setCoefficient2x(0.1808);
      curve.setCoefficient3xPOW2(.0256);
      curve.setMinimumValueofx(.5);
      curve.setMaximumValueofx(1.5);
      setEnergyInputRatioFunctionofFlowFractionCurve(curve);
    }

    {
      CurveQuadratic curve(model);
      curve.setCoefficient1Constant(0.85);
      curve.setCoefficient2x(0.15);
      curve.setCoefficient3xPOW2(0.0);
      curve.setMinimumValueofx(0.0);
      curve.setMaximumValueofx(1.0);
      setPartLoadFractionCorrelationCurve(curve);
    }
  }

  IddObjectType CoilPerformanceDXCooling::iddObjectType() {
    return {IddObjectType::OS_CoilPerformance_DX_Cooling};
  }

  std::vector<std::string> CoilPerformanceDXCooling::condenserTypeValues() {
    return getIddKeyNames(IddFactory::instance().getObject(iddObjectType()).get(), OS_CoilPerformance_DX_CoolingFields::CondenserType);
  }

  boost::optional<double> CoilPerformanceDXCooling::grossRatedTotalCoolingCapacity() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->grossRatedTotalCoolingCapacity();
  }

  bool CoilPerformanceDXCooling::isGrossRatedTotalCoolingCapacityAutosized() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->isGrossRatedTotalCoolingCapacityAutosized();
  }

  boost::optional<double> CoilPerformanceDXCooling::grossRatedSensibleHeatRatio() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->grossRatedSensibleHeatRatio();
  }

  bool CoilPerformanceDXCooling::isGrossRatedSensibleHeatRatioAutosized() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->isGrossRatedSensibleHeatRatioAutosized();
  }

  double CoilPerformanceDXCooling::grossRatedCoolingCOP() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->grossRatedCoolingCOP();
  }

  boost::optional<double> CoilPerformanceDXCooling::ratedAirFlowRate() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->ratedAirFlowRate();
  }

  bool CoilPerformanceDXCooling::isRatedAirFlowRateAutosized() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->isRatedAirFlowRateAutosized();
  }

  double CoilPerformanceDXCooling::fractionofAirFlowBypassedAroundCoil() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->fractionofAirFlowBypassedAroundCoil();
  }

  Curve CoilPerformanceDXCooling::totalCoolingCapacityFunctionofTemperatureCurve() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->totalCoolingCapacityFunctionofTemperatureCurve();
  }

  Curve CoilPerformanceDXCooling::totalCoolingCapacityFunctionofFlowFractionCurve() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->totalCoolingCapacityFunctionofFlowFractionCurve();
  }

  Curve CoilPerformanceDXCooling::energyInputRatioFunctionofTemperatureCurve() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->energyInputRatioFunctionofTemperatureCurve();
  }

  Curve CoilPerformanceDXCooling::energyInputRatioFunctionofFlowFractionCurve() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->energyInputRatioFunctionofFlowFractionCurve();
  }

  Curve CoilPerformanceDXCooling::partLoadFractionCorrelationCurve() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->partLoadFractionCorrelationCurve();
  }

  double CoilPerformanceDXCooling::nominalTimeforCondensateRemovaltoBegin() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->nominalTimeforCondensateRemovaltoBegin();
  }

  double CoilPerformanceDXCooling::ratioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->ratioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity();
  }

  double CoilPerformanceDXCooling::maximumCyclingRate() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->maximumCyclingRate();
  }

  double CoilPerformanceDXCooling::latentCapacityTimeConstant() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->latentCapacityTimeConstant();
  }

  boost::optional<Node> CoilPerformanceDXCooling::condenserAirInletNode() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->condenserAirInletNode();
  }

  std::string CoilPerformanceDXCooling::condenserType() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->condenserType();
  }

  double CoilPerformanceDXCooling::evaporativeCondenserEffectiveness() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->evaporativeCondenserEffectiveness();
  }

  boost::optional<double> CoilPerformanceDXCooling::evaporativeCondenserAirFlowRate() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->evaporativeCondenserAirFlowRate();
  }

  bool CoilPerformanceDXCooling::isEvaporativeCondenserAirFlowRateAutosized() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->isEvaporativeCondenserAirFlowRateAutosized();
  }

  boost::optional<double> CoilPerformanceDXCooling::evaporativeCondenserPumpRatedPowerConsumption() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->evaporativeCondenserPumpRatedPowerConsumption();
  }

  bool CoilPerformanceDXCooling::isEvaporativeCondenserPumpRatedPowerConsumptionAutosized() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->isEvaporativeCondenserPumpRatedPowerConsumptionAutosized();
  }

  boost::optional<Curve> CoilPerformanceDXCooling::sensibleHeatRatioFunctionofTemperatureCurve() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->sensibleHeatRatioFunctionofTemperatureCurve();
  }

  boost::optional<Curve> CoilPerformanceDXCooling::sensibleHeatRatioFunctionofFlowFractionCurve() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->sensibleHeatRatioFunctionofFlowFractionCurve();
  }

  bool CoilPerformanceDXCooling::setGrossRatedTotalCoolingCapacity(double grossRatedTotalCoolingCapacity) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setGrossRatedTotalCoolingCapacity(grossRatedTotalCoolingCapacity);
  }

  void CoilPerformanceDXCooling::autosizeGrossRatedTotalCoolingCapacity() {
    getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizeGrossRatedTotalCoolingCapacity();
  }

  bool CoilPerformanceDXCooling::setGrossRatedSensibleHeatRatio(double grossRatedSensibleHeatRatio) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setGrossRatedSensibleHeatRatio(grossRatedSensibleHeatRatio);
  }

  void CoilPerformanceDXCooling::autosizeGrossRatedSensibleHeatRatio() {
    getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizeGrossRatedSensibleHeatRatio();
  }

  bool CoilPerformanceDXCooling::setGrossRatedCoolingCOP(double grossRatedCoolingCOP) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setGrossRatedCoolingCOP(grossRatedCoolingCOP);
  }

  bool CoilPerformanceDXCooling::setRatedAirFlowRate(double ratedAirFlowRate) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setRatedAirFlowRate(ratedAirFlowRate);
  }

  void CoilPerformanceDXCooling::autosizeRatedAirFlowRate() {
    getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizeRatedAirFlowRate();
  }

  bool CoilPerformanceDXCooling::setFractionofAirFlowBypassedAroundCoil(double fractionofAirFlowBypassedAroundCoil) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setFractionofAirFlowBypassedAroundCoil(fractionofAirFlowBypassedAroundCoil);
  }

  bool CoilPerformanceDXCooling::setTotalCoolingCapacityFunctionofTemperatureCurve(const Curve& curve) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setTotalCoolingCapacityFunctionofTemperatureCurve(curve);
  }

  bool CoilPerformanceDXCooling::setTotalCoolingCapacityFunctionofFlowFractionCurve(const Curve& curve) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setTotalCoolingCapacityFunctionofFlowFractionCurve(curve);
  }

  bool CoilPerformanceDXCooling::setEnergyInputRatioFunctionofTemperatureCurve(const Curve& curve) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setEnergyInputRatioFunctionofTemperatureCurve(curve);
  }

  bool CoilPerformanceDXCooling::setEnergyInputRatioFunctionofFlowFractionCurve(const Curve& curve) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setEnergyInputRatioFunctionofFlowFractionCurve(curve);
  }

  bool CoilPerformanceDXCooling::setPartLoadFractionCorrelationCurve(const Curve& curve) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setPartLoadFractionCorrelationCurve(curve);
  }

  bool CoilPerformanceDXCooling::setNominalTimeforCondensateRemovaltoBegin(double nominalTimeforCondensateRemovaltoBegin) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setNominalTimeforCondensateRemovaltoBegin(nominalTimeforCondensateRemovaltoBegin);
  }

  bool CoilPerformanceDXCooling::setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(
    double ratioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setRatioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity(
      ratioofInitialMoistureEvaporationRateandSteadyStateLatentCapacity);
  }

  bool CoilPerformanceDXCooling::setMaximumCyclingRate(double maximumCyclingRate) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setMaximumCyclingRate(maximumCyclingRate);
  }

  bool CoilPerformanceDXCooling::setLatentCapacityTimeConstant(double latentCapacityTimeConstant) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setLatentCapacityTimeConstant(latentCapacityTimeConstant);
  }

  bool CoilPerformanceDXCooling::setCondenserAirInletNode(const Node& node) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setCondenserAirInletNode(node);
  }

  void CoilPerformanceDXCooling::resetCondenserAirInletNode() {
    getImpl<detail::CoilPerformanceDXCooling_Impl>()->resetCondenserAirInletNode();
  }

  bool CoilPerformanceDXCooling::setCondenserType(const std::string& condenserType) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setCondenserType(condenserType);
  }

  bool CoilPerformanceDXCooling::setEvaporativeCondenserEffectiveness(double evaporativeCondenserEffectiveness) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setEvaporativeCondenserEffectiveness(evaporativeCondenserEffectiveness);
  }

  bool CoilPerformanceDXCooling::setEvaporativeCondenserAirFlowRate(double evaporativeCondenserAirFlowRate) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setEvaporativeCondenserAirFlowRate(evaporativeCondenserAirFlowRate);
  }

  void CoilPerformanceDXCooling::autosizeEvaporativeCondenserAirFlowRate() {
    getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizeEvaporativeCondenserAirFlowRate();
  }

  bool CoilPerformanceDXCooling::setEvaporativeCondenserPumpRatedPowerConsumption(double evaporativeCondenserPumpRatedPowerConsumption) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setEvaporativeCondenserPumpRatedPowerConsumption(
      evaporativeCondenserPumpRatedPowerConsumption);
  }

  void CoilPerformanceDXCooling::autosizeEvaporativeCondenserPumpRatedPowerConsumption() {
    getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizeEvaporativeCondenserPumpRatedPowerConsumption();
  }

  bool CoilPerformanceDXCooling::setSensibleHeatRatioFunctionofTemperatureCurve(const Curve& curve) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setSensibleHeatRatioFunctionofTemperatureCurve(curve);
  }

  void CoilPerformanceDXCooling::resetSensibleHeatRatioFunctionofTemperatureCurve() {
    getImpl<detail::CoilPerformanceDXCooling_Impl>()->resetSensibleHeatRatioFunctionofTemperatureCurve();
  }

  bool CoilPerformanceDXCooling::setSensibleHeatRatioFunctionofFlowFractionCurve(const Curve& curve) {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->setSensibleHeatRatioFunctionofFlowFractionCurve(curve);
  }

  void CoilPerformanceDXCooling::resetSensibleHeatRatioFunctionofFlowFractionCurve() {
    getImpl<detail::CoilPerformanceDXCooling_Impl>()->resetSensibleHeatRatioFunctionofFlowFractionCurve();
  }

  /// @cond
  CoilPerformanceDXCooling::CoilPerformanceDXCooling(std::shared_ptr<detail::CoilPerformanceDXCooling_Impl> impl) : ParentObject(std::move(impl)) {}
  /// @endcond

  boost::optional<double> CoilPerformanceDXCooling::autosizedGrossRatedTotalCoolingCapacity() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizedGrossRatedTotalCoolingCapacity();
  }

  boost::optional<double> CoilPerformanceDXCooling::autosizedGrossRatedSensibleHeatRatio() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizedGrossRatedSensibleHeatRatio();
  }

  boost::optional<double> CoilPerformanceDXCooling::autosizedRatedAirFlowRate() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizedRatedAirFlowRate();
  }

  boost::optional<double> CoilPerformanceDXCooling::autosizedEvaporativeCondenserAirFlowRate() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizedEvaporativeCondenserAirFlowRate();
  }

  boost::optional<double> CoilPerformanceDXCooling::autosizedEvaporativeCondenserPumpRatedPowerConsumption() const {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosizedEvaporativeCondenserPumpRatedPowerConsumption();
  }

  void CoilPerformanceDXCooling::autosize() {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->autosize();
  }

  void CoilPerformanceDXCooling::applySizingValues() {
    return getImpl<detail::CoilPerformanceDXCooling_Impl>()->applySizingValues();
  }

}  // namespace model
}  // namespace openstudio
